package com.tyros.common.kit.web.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tyros.common.kit.bean.BeanCopyKit;
import com.tyros.common.kit.web.model.PageQuery;
import com.tyros.common.kit.web.model.PageResult;
import com.tyros.common.kit.web.service.IBaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.GenericTypeResolver;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;

/**
 * 基本Service接口，封装通用数据库操作
 *
 * @param <M> mapper
 * @param <D> DTO
 * @param <T> 实体类
 * @author zhangshuaiyin
 * @date 2022/1/15 21:15
 */
@Slf4j
@SuppressWarnings("ALL")
public abstract class AbstractBaseServiceImpl<M extends BaseMapper<T>, T, D> extends ServiceImpl<M, T> implements IBaseService<D, T> {

    private Class<D> dtoClz;

    private Class<T> entityClz;

    protected Class<D> getDTOClz() {
        if (dtoClz == null) {
            dtoClz = (Class<D>) getSuperClassGenericType(this.getClass(), IBaseService.class, 0);
        }
        return dtoClz;
    }

    protected Class<T> getEntityClz() {
        if (entityClz == null) {
            entityClz = currentModelClass();
        }
        return entityClz;
    }

    /**
     * 当前方法来自mybatis-plus 3.4.3.2
     * 获取父类的泛型类型
     * <p>
     * see {@link com.baomidou.mybatisplus.core.toolkit.ReflectionKit#getSuperClassGenericType(Class, Class, int)}
     *
     * @param clazz Class 对象
     * @param index 索引
     * @return 父类的泛型类型
     */
    public static Class<?> getSuperClassGenericType(final Class<?> clazz, final Class<?> genericIfc, final int index) {
        Class<?>[] typeArguments = GenericTypeResolver.resolveTypeArguments(ClassUtils.getUserClass(clazz), genericIfc);
        return null == typeArguments ? null : typeArguments[index];
    }

    @Override
    public D get(Serializable id) {
        return BeanCopyKit.copy(this.getById(id), getDTOClz());
    }

    @Override
    public D selectOne(Wrapper<T> queryWrapper) {
        return BeanCopyKit.copy(this.getOne(queryWrapper), getDTOClz());
    }

    @Override
    public D selectOne(D dto) {
        return BeanCopyKit.copy(this.getOne(queryWrapper(dto)), getDTOClz());
    }

    @Override
    public List<D> list(D dto) {
        return BeanCopyKit.copyList(this.list(queryWrapper(dto)), getDTOClz());
    }

    @Override
    public boolean update(D dto) {
        return updateById(BeanCopyKit.copy(dto, getEntityClz()));
    }

    @Override
    public boolean delete(List<Serializable> ids) {
        return removeByIds(ids);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean insert(D dto) {
        Class<T> entityClz = getEntityClz();
        T entity = BeanCopyKit.copy(dto, entityClz);
        boolean save = save(entity);
        if (save) {
            BeanCopyKit.fill(entity, dto);
        }
        return save;
    }

    @Override
    public boolean insertOrUpdate(D dto) {
        if (null == dto) {
            return false;
        } else {
            T entity = BeanCopyKit.copy(dto, getEntityClz());
            Class<?> cls = entity.getClass();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
            Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
            String keyProperty = tableInfo.getKeyProperty();
            Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
            Object idVal = ReflectionKit.getFieldValue(entity, tableInfo.getKeyProperty());
            return !StringUtils.checkValNull(idVal) && !Objects.isNull(this.getById((Serializable) idVal)) ? this.updateById(entity) : insert(dto);
        }
    }

    @Override
    public PageResult<D> page(PageQuery pageQuery) {
        return page(pageQuery, () -> pageWrapper(pageQuery));
    }

    @Override
    public <R> PageResult<R> page(PageQuery pageQuery, Class<R> targetClz) {
        IPage<T> page = baseMapper.selectPage(buildPage(pageQuery), pageWrapper(pageQuery));
        return new PageResult<>(page.getTotal(), BeanCopyKit.copyList(page.getRecords(), targetClz));
    }

    @Override
    public boolean insertBatch(List<D> list) {
        List<T> ts = BeanCopyKit.copyList(list, getEntityClz());
        return this.saveBatch(ts);
    }

    /**
     * 分页构造器
     *
     * @param pageQuery 分页对象
     * @param supplier  分页条件
     * @return 分页结果
     */
    protected PageResult<D> page(PageQuery pageQuery, Supplier<LambdaQueryWrapper<T>> supplier) {
        IPage<T> page = baseMapper.selectPage(buildPage(pageQuery), supplier.get());
        return new PageResult<>(page.getTotal(), BeanCopyKit.copyList(page.getRecords(), getDTOClz()));
    }

    /**
     * 构建分页参数
     *
     * @param pageQuery 分页查询参数
     */
    protected IPage<T> buildPage(PageQuery pageQuery) {
        if (pageQuery.getPageNum() < 1) {
            pageQuery.setPageNum(PageQuery.PAGE_NUM);
        }
        if (pageQuery.getPageSize() < 1) {
            pageQuery.setPageSize(PageQuery.PAGE_SIZE);
        }
        return new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
    }

    /**
     * 基础的查询条件构造器
     *
     * @return 查询条件构造器
     */
    public LambdaQueryWrapper<T> queryWrapper() {
        return Wrappers.<T>lambdaQuery();
    }

    /**
     * 基础的更新条件构造器
     *
     * @return 更新条件构造器
     */
    public LambdaUpdateWrapper<T> updateWrapper() {
        return Wrappers.<T>lambdaUpdate();
    }

    /**
     * 构建分页查询条件包装器
     * <p>
     * 重写该方法实现分页的自定义排序
     *
     * @param query 分页参数, 实现类接收前端传递的分页条件
     * @return 根据分页条件封装查询Wrapper
     */
    protected LambdaQueryWrapper<T> pageWrapper(PageQuery query) {
        return Wrappers.<T>lambdaQuery();
    }

    /**
     * 根据 DTO 创建查询条件构造器，以非空字段作为查询条件
     *
     * @param dto 查询条件
     * @return 查询条件构造器
     */
    public Wrapper queryWrapper(D dto) {
        QueryWrapper<Object> wrapper = Wrappers.query();
        // 遍历 DTO 的属性封装非空值的查询条件
        Class<D> dtoClz = getDTOClz();
        List<Field> fieldList = ReflectionKit.getFieldList(dtoClz);
        fieldList.forEach(field -> {
            Object value = ReflectionKit.getFieldValue(dto, field.getName());
            wrapper.eq(Objects.nonNull(value), field.getName(), value);
        });
        return wrapper;
    }
}
